feat(org-lens): org lens project detail page UI#1028
Conversation
|
Note Reviews pausedIt looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the Use the following commands to manage reviews:
Use the checkboxes below for quick actions:
WalkthroughAdds an Org Lens project-detail route, API, demo payloads, page rendering logic, and E2E coverage, plus a Valkey JSON cache miss handling tweak. ChangesOrg Project Detail
Valkey typing
Estimated code review effort🎯 4 (Complex) | ⏱️ ~60 minutes Possibly related PRs
Suggested labels
🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✨ Finishing Touches🧪 Generate unit tests (beta)
Comment |
🚀 Deployment StatusYour branch has been deployed to: https://ui-pr-1028.dev.v2.cluster.linuxfound.info Deployment Details:
The deployment will be automatically removed when this PR is closed. |
There was a problem hiding this comment.
Pull request overview
Adds the Org Lens Project Detail page under /org/projects/:projectSlug, introducing shared payload contracts plus a frontend demo-data seam and new UI (tabs, charts, drawer details) with query-param state. The PR also wires a new org route and updates navigation/telemetry touchpoints, and adds Playwright coverage.
Changes:
- Add shared
OrgLensProjectDetailResponsecontracts and export them from@lfx-one/shared/interfaces. - Implement the Org Project Detail page UI (tabs, influence cards, leaderboards, stacked trend chart, card-detail drawer) backed by demo fixtures and a new Angular service.
- Add org routing + E2E tests; minor Valkey cache read typing tweak.
Reviewed changes
Copilot reviewed 13 out of 13 changed files in this pull request and generated 8 comments.
Show a summary per file
| File | Description |
|---|---|
| packages/shared/src/interfaces/org-lens-project-detail.interface.ts | Adds shared API contracts for Org Lens Project Detail payloads. |
| packages/shared/src/interfaces/index.ts | Re-exports the new Project Detail interfaces from the interfaces barrel. |
| apps/lfx-one/src/server/services/valkey.service.ts | Tightens getJson typing for Valkey reads and null handling. |
| apps/lfx-one/src/app/shared/services/org-lens-project-detail.service.ts | Adds a frontend service seam for loading project detail (currently demo-backed). |
| apps/lfx-one/src/app/shared/services/org-lens-project-detail.demo-data.ts | Adds large deterministic demo fixture generator for the project detail page. |
| apps/lfx-one/src/app/modules/dashboards/org/org-project-detail/org-project-detail.component.ts | Implements page state machine, query-param persistence, leaderboard ranking, and chart dataset construction. |
| apps/lfx-one/src/app/modules/dashboards/org/org-project-detail/org-project-detail.component.html | Implements page shell, hero, tabs, influence cards, leaderboards, trend chart, and drawer markup with testids. |
| apps/lfx-one/src/app/modules/dashboards/org/org-project-detail/org-project-detail-tab-bar.component.ts | Adds the shared tab/metric/range control bar with keyboard navigation. |
| apps/lfx-one/src/app/modules/dashboards/org/org-project-detail/org-project-detail-tab-bar.component.html | Tab bar template (tablist + metric toggle + time-range pills). |
| apps/lfx-one/src/app/modules/dashboards/org/components/org-overview-foundations-and-projects/org-overview-foundations-and-projects.component.ts | Adds telemetry + navigation handlers intended to open project detail from overview rows. |
| apps/lfx-one/src/app/layouts/main-layout/main-layout.component.ts | Re-enables “Projects” entry in Org Lens sidebar section. |
| apps/lfx-one/src/app/app.routes.ts | Adds the /org/projects/:projectSlug route entry. |
| apps/lfx-one/e2e/org-project-detail.spec.ts | Adds Playwright E2E coverage for project detail page behaviors and URL persistence. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
Address review comments from copilot-pull-request-reviewer, copilot-swe-agent: - org-project-detail.component.ts: make scoreColumnLabel dynamic using TIME_RANGE_MONTHS[timeRange()] so Activity column shows correct period for 2y/all ranges, not hard-coded (12mo) (per copilot-pull-request-reviewer) - org-project-detail.component.ts: fix All others stacked trend to use the SUM of scores, not average — average understates the aggregate (per copilot-swe-agent) - org-overview-foundations-and-projects.component.html: restore project row click/keydown handlers now that the detail drilldown page is built; remove the deferred-until-ready comment (per copilot-pull-request-reviewer) - org-lens-project-detail.service.ts: remove Generated-with comment (per copilot-pull-request-reviewer) - e2e/org-project-detail.spec.ts: move project-detail-trend-group assertion to the leaderboards test where it is actually rendered (per copilot-swe-agent) - e2e/org-project-detail.spec.ts: remove stale ?score=/ from JSDoc — only ?metric= is used (per copilot-swe-agent) Resolves 5 review threads; 2 threads addressed via responses only (breadcrumb/CTA route and SSR endpoint are intentional by design). Signed-off-by: daniel qualls <dqualls@linuxfoundation.org>
Review Feedback AddressedChanges Made
Threads Resolved7 of 7 unresolved threads addressed in this iteration. |
Address review comment from copilot-pull-request-reviewer: - org-lens-project-detail.demo-data.ts: use Object.hasOwn(SEEDS, projectSlug) before indexing SEEDS, so keys inherited from Object.prototype (constructor, toString, valueOf, etc.) correctly fall through to the not-found path instead of returning a truthy prototype value. Resolves 1 review thread. Signed-off-by: daniel qualls <dqualls@linuxfoundation.org>
Address review comments from copilot-pull-request-reviewer: - org-project-detail.component.ts: breadcrumb root now labeled "Projects" and links to /org/projects instead of /org/overview - org-project-detail.component.html: not-found CTA and breadcrumb home routerLink now point to /org/projects instead of /org/overview Matches the page's location as a sub-page of /org/projects/:projectSlug. Resolves 3 review threads. Signed-off-by: daniel qualls <dqualls@linuxfoundation.org>
Address review comments from copilot-pull-request-reviewer: - org-lens-project-detail.constants.ts: prefix DEFAULT_METRIC, VALID_METRICS, DEFAULT_TIME_RANGE, VALID_TIME_RANGES, HEALTH_TAG, BAND_TAG, METRIC_OPTIONS, TIME_RANGE_OPTIONS, TIME_RANGE_MONTHS, STACKED_PALETTE with PD_ so the shared barrel export can't collide with an identically-named constant from another feature - org-lens-project-detail.interface.ts, org-lens-project-detail.demo-data.ts: drop hero.sourceUrl, hero.sourceLabel, hero.lastUpdated, and the unused `trend` payload (OrgLensProjectTrendPoint + trendSeries()/trailing12Months() generators) — none are read by the component, which builds its trend chart from `leaderboard` via buildStackedTrend() - org-project-detail.component.ts: corrected the class doc comment — the Projects table and Influence Summary cards are not yet wired to this route; only the Org Overview Foundations & Projects table is Resolves 3 review threads. Signed-off-by: daniel qualls <dqualls@linuxfoundation.org>
Address review comment from copilot-pull-request-reviewer[bot]: - org-project-detail.component.ts: orgUid$ no longer filters out until an org is selected; falls back to 'demo-org' like the sibling org-projects.component.ts does, so the page renders in demo mode instead of hanging on the loading skeleton indefinitely. Signed-off-by: daniel qualls <dqualls@linuxfoundation.org>
Address review comments from copilot-pull-request-reviewer[bot]: - org-project-detail.component.ts: reverted the demo-org uid fallback added in 4b0dc69 — this page hits a real HTTP endpoint validated by assertOrgUid (18-char Salesforce Account.Id), so the fallback would reliably 400 instead of rendering. orgUid$ now waits for a real org uid again, matching the other HTTP-backed Org Lens pages. - org-overview-foundations-and-projects.component.html: added an aria-label to the clickable project row so screen readers announce what activating it does, matching the pattern in contributors.component.html. Signed-off-by: daniel qualls <dqualls@linuxfoundation.org>
audigregorie
left a comment
There was a problem hiding this comment.
Code Review Summary
Solid end-to-end delivery of the Org Lens Project Detail page: shared contracts, a clean server demo-data seam, URL-persisted tab/metric/range state, keyboard-accessible tab bar, and meaningful Playwright coverage. Prior review rounds already addressed routing, breadcrumb targets, dynamic activity column labels, and the stacked-trend "All others" aggregation.
Remaining gaps are mostly polish and consistency with newer Org Lens patterns.
Major — outside the diff
- No-org empty state: Unlike
org-contributions/org-people, this page never gates onhasCompany. WhenselectedAccount().uidis missing,initDetailStream()never emits and the skeleton spins indefinitely. - E2E coverage gaps: Tests deep-link to
/org/projects/kubernetesbut do not cover (a) navigation from Org Overview project rows, (b) opening an influence-card drawer, or (c) the no-company empty state.
What's done well
- Demo data lives server-side with a thin controller/service/route trio — easy Snowflake swap later.
- Query-param persistence for tab, metric, and range with sensible defaults stripped from the URL.
- Side-by-side leaderboard tables with viewing-org highlight, search, and paginator.
- Tab bar implements roving tabindex + Arrow/Home/End keyboard support.
Address review comments from @audigregorie: - org-project-detail.component.ts/.html: gate the page behind a hasCompany empty state so it no longer loads indefinitely when no org is selected; removed the unused orgName computed with its misleading 'Acme Corp' fallback - org-project-detail.component.html: swapped both raw <input pInputText> leaderboard search boxes for lfx-input-text, backed by a reactive FormGroup with debounced valueChanges - org-project-detail.component.ts: replaced the innerHTML-based Chart.js external tooltip with safe DOM construction (createElement/textContent) - org-project-detail.component.html: drawer's primary stat now falls back to '—' when empty, matching the card-level pattern - org-project-detail.component.html: leaderboard detail rows now track by a stable content-derived key instead of $index - org-project-detail.spec.ts: updated search selector to match the lfx-input-text wrapper's data-test attribute Resolves 6 review threads; OrgLensScoreType doc comment (thread 7) was already accurate, no code change needed. Signed-off-by: daniel qualls <dqualls@linuxfoundation.org>
Review Feedback AddressedCommit: 15b4d5d Changes Made
No Change Needed
Threads Resolved7 of 7 threads addressed in this iteration. Still Open (not in scope for this round)
|
# Conflicts: # packages/shared/src/constants/index.ts
Fixes the failing "Code Quality Checks" CI job on PR #1028, which flagged formatting drift in these two files after the recent review fixes and merge from main. Signed-off-by: daniel qualls <dqualls@linuxfoundation.org>
Address review comments from copilot-pull-request-reviewer[bot]: - org-project-detail.component.ts: corrected a stale JSDoc comment above rowKey() that described scroll-arrow visibility behavior (which actually lives in onTrackScroll()/refreshArrows()) — now documents what rowKey() does. - PR description: fixed the stale Ecosystem Influence card list to match the shipped cards (Event Sponsorships, Meetup Attendance instead of the never-implemented Training Completions and Mailing List Posts). Resolves 2 review threads. Signed-off-by: daniel qualls <dqualls@linuxfoundation.org>
Address review comments from copilot-pull-request-reviewer[bot]:
- org-lens-project-detail.demo-data.ts: capitalized the leaderboard
viewing-org fallback copy ("Your Organization") to match the title
case already used on org-overview.component.ts and the
org-lens-project-detail.controller.ts fallback.
- Kept ProjectDetailSeed/Caption as `interface` rather than switching
to `type` — the repo's `@typescript-eslint/consistent-type-definitions`
ESLint rule requires `interface` for object shapes, so the suggested
change would fail lint. No code change needed for that finding.
Resolves 1 review thread; 2 threads addressed with an explanation.
Signed-off-by: daniel qualls <dqualls@linuxfoundation.org>
Address review comments from copilot[bot]: - org-project-detail.component.ts: technicalCards/ecosystemCards now always slice influence-card sparklines at the default 12-month window instead of the URL-persisted timeRange (12/24/36), matching the OrgLensProjectInfluenceCard.sparkline contract (fixed 12 monthly bins). timeRange now only affects the Leaderboards tab. - org-project-detail.component.ts: scoreColumnLabel and drawerTimeRangeLabel special-case range=all to render "Activity (All time)" / "All time" instead of "Activity (36mo)" / "Last 36 months". - org-lens-project-detail.demo-data.ts: extracted a `plural(n, singular, plural)` helper and used it to remove three nested ternaries in the maintainers, meeting-attendance, and board-members caption builders. Resolves 5 review threads. Signed-off-by: daniel qualls <dqualls@linuxfoundation.org>
Address review comments from copilot[bot]: - packages/shared/src/constants/org-lens-project-detail.constants.ts: align PD_HEALTH_TAG.healthy severity to 'info' to match HEALTH_SCORE_SEVERITY.healthy on the Org Lens Projects page, so the "Healthy" badge renders the same color across both pages (per copilot[bot]) - apps/lfx-one/src/server/controllers/org-lens-project-detail.controller.ts: add assertProjectSlug validation mirroring assertFoundationSlug in org-lens-memberships.controller.ts, hardening the projectSlug path param against malformed input before the real Snowflake backend replaces the fixture lookup (per copilot[bot]) Resolves 2 review threads. Signed-off-by: daniel qualls <dqualls@linuxfoundation.org>
🧹 Deployment RemovedThe deployment for PR #1028 has been removed. |
| </div> | ||
| <div class="flex min-w-0 flex-col gap-0.5"> | ||
| <h2 class="text-base font-semibold text-gray-900" data-testid="influence-card-detail-title">{{ card.title }}</h2> | ||
| <p class="text-sm text-gray-500">{{ drawerTimeRangeLabel() }} · {{ card.scopeLabel ?? 'All projects' }}</p> |
| <tr class="border-b border-gray-200 text-left text-xs font-medium text-gray-500"> | ||
| <th scope="col" class="w-12 px-3 py-3">#</th> | ||
| <th scope="col" class="px-3 py-3">Organization</th> | ||
| <th scope="col" class="whitespace-nowrap px-3 py-3 text-right">{{ scoreColumnLabel() }}</th> |
Business case: company can view their involvement in a project in a detailed way that adds up to influence in the project and compares influence to other companies. UI only with demo data. Includes both influence detail tab and leaderboards tab.
This pull request introduces the Org Lens Project Detail sub-page (LFXV2-1885) to the application, enabling users to view detailed analytics and leaderboard data for individual projects within an organization. It includes the new route and navigation entry, a complete UI with tabbed navigation, metric and time range toggles, demo data integration, and comprehensive E2E tests. Additionally, it restores interactivity for project rows in the Org Overview, allowing users to drill down into project details.
Org Lens Project Detail Feature Implementation:
/org/projects/:projectSlugroute, navigation entry, and associated metadata, enabling direct navigation to project detail pages. [1] [2]OrgProjectDetailServiceto provide demo data for the project detail page, designed for easy replacement with a real backend API in the future.OrgProjectDetailTabBarComponentfor tabbed navigation, including keyboard accessibility, metric toggles, and time range selection. [1] [2]Org Overview Interactivity Restoration:
Supporting and Miscellaneous Updates:
Summary
/org/projects/:projectSlug, opened from the Overview's Foundations & Projects tableorg-lens-project-detail.demo-data.ts) via a new service + controller + route — demo company data only; real Snowflake integration is a separate story. Note: there is no static JSON fixture; all demo payloads are generated server-side in TypeScript.?tab=,?metric=,?range=)Our Influence tab:
Leaderboards tab:
Shell:
<p-skeleton>loading states andlfx-empty-stateerror/not-found panelsdata-testidattributes; E2E coverage addedPR size rationale
This PR is ~2,500 lines. The branch builds the complete Project Detail page end-to-end (scaffold → hero → influence cards → trend chart → leaderboards → e2e → polish). The intermediate commits are not independently shippable — routing and the data layer land without visible UI until the component is complete. Splitting at a natural seam (e.g., scaffold-only or data-layer-only) would merge dead routes that don't render. The full page is the atomic unit of work here, consistent with how the sibling Membership Detail page (PR #921) was delivered.
Test plan
/org/overview, expand a foundation, click an LF project row → lands on/org/projects/<slug>?tab=and survives page reload?range=query param and persist on reload/org/projectsyarn e2e🤖 Generated with Claude Code